package com.eazy.lksy.web.core.dao;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.sql.Types;
import java.util.*;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.eazy.lksy.core.annotation.*;
import com.eazy.lksy.core.annotation.Date;
import com.eazy.lksy.web.exception.ServiceException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.RowMapper;

public class CommonDaoImpl<T> extends BaseDao implements CommonDao<T> {

	/** 设置一些操作的常量 */
	public static final String SQL_INSERT = "insert";
	public static final String SQL_UPDATE = "update";
	public static final String SQL_DELETE = "delete";

	protected Table table;
	private Class<T> entityClass;

	public CommonDaoImpl() {
		ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
		entityClass = (Class<T>) type.getActualTypeArguments()[0];
		if(ObjectUtil.isEmpty(entityClass)) {
			throw new ServiceException("通用类型dao没有指定具体entity");
		}
		table = entityClass.getAnnotation(Table.class);
	}

	@Override
	public List<T> select() {
		RowMapper<T> rowMapper = BeanPropertyRowMapper.newInstance(entityClass);
		return dao.query("select * from " + table.name() , rowMapper);
	}

	@Override
	public boolean insert(T entity) {
		String sql = this.makeSql(SQL_INSERT);
		Object[] args = this.setArgs(entity, SQL_INSERT);
		int[] argTypes = this.setArgTypes(entity, SQL_INSERT);
		return dao.update(sql.toString(), args, argTypes) > 1;
	}

	@Override
	public T selectById(Serializable id) {
		RowMapper<T> rowMapper = BeanPropertyRowMapper.newInstance(entityClass);
		return dao.queryForObject("select * from " + table.name() + " where id=" + id,rowMapper);
	}

	@Override
	public int count() {
		return dao.queryForInt("select count(*) from " + table.name());
	}

	@Override
	public boolean deleteById(Serializable id) {
		return dao.update("delete from " + table.name() + " where id=?",id) > 1;
	}

	@Override
	public boolean update(T entity) {
		String sql = this.makeSql(SQL_UPDATE);
		Object[] args = this.setArgs(entity, SQL_UPDATE);
		int[] argTypes = this.setArgTypes(entity, SQL_UPDATE);
		return dao.update(sql.toString(), args, argTypes) > 1;
	}
	// 组装SQL
	private String makeSql(String sqlFlag) {
		StringBuffer sql = new StringBuffer();
		Field[] fields = entityClass.getDeclaredFields();
		if (sqlFlag.equals(SQL_INSERT)) {
			sql.append(" INSERT INTO " + table.name());
			sql.append("(");
			for (int i = 0; fields != null && i < fields.length; i++) {
				fields[i].setAccessible(true); // 暴力反射
				Id id = fields[i].getAnnotation(Id.class);
				if(id !=null && id.value().equals("auto")) {
					//走数据库自增
					continue;
				}
				Date date = fields[i].getAnnotation(Date.class);
				if(date !=null && date.type().equals("auto")) {
					continue;
				}
				Ignore ignore = fields[i].getAnnotation(Ignore.class);
				if(ignore !=null) {
					continue;
				}
				String column = fields[i].getName();
				//使用我们自定义的注解和数据库映射
				Column columnAnn = fields[i].getAnnotation(Column.class);
				if(columnAnn !=null) {
					column = columnAnn.field();
				}
				sql.append(column).append(",");
			}
			sql = sql.deleteCharAt(sql.length() - 1);
			sql.append(") VALUES (");
			for (int i = 0; fields != null && i < fields.length; i++) {
				fields[i].setAccessible(true); // 暴力反射
				Id id = fields[i].getAnnotation(Id.class);
				if(id !=null && id.value().equals("auto")) {
					//走数据库自增
					continue;
				}
				Date date = fields[i].getAnnotation(Date.class);
				if(date !=null && date.type().equals("auto")) {
					continue;
				}
				Ignore ignore = fields[i].getAnnotation(Ignore.class);
				if(ignore !=null) {
					continue;
				}
				sql.append("?,");
			}
			sql = sql.deleteCharAt(sql.length() - 1);
			sql.append(")");
		} else if (sqlFlag.equals(SQL_UPDATE)) {
			sql.append(" UPDATE " + table.name() + " SET ");
			for (int i = 0; fields != null && i < fields.length; i++) {
				fields[i].setAccessible(true); // 暴力反射
				String column = fields[i].getName();
				if (column.equals("id")) { // id 代表主键
					continue;
				}
				Ignore ignore = fields[i].getAnnotation(Ignore.class);
				if(ignore !=null) {
					continue;
				}
				//使用我们自定义的注解和数据库映射
				Column columnAnn = fields[i].getAnnotation(Column.class);
				if(columnAnn !=null) {
					column = columnAnn.field();
				}

				sql.append(column).append("=").append("?,");
			}
			sql = sql.deleteCharAt(sql.length() - 1);
			sql.append(" WHERE id=?");
		} else if (sqlFlag.equals(SQL_DELETE)) {
			sql.append(" DELETE FROM " + entityClass.getSimpleName() + " WHERE id=?");
		}
		System.out.println("SQL=" + sql);
		return sql.toString();

	}

	// 设置参数
	private Object[] setArgs(T entity, String sqlFlag) {
		Field[] fields = entityClass.getDeclaredFields();
		if (sqlFlag.equals(SQL_INSERT)) {
			List<Object> objects = new ArrayList<>();
			Object[] args = new Object[fields.length];
			for (int i = 0; args != null && i < args.length; i++) {
				try {
					fields[i].setAccessible(true); // 暴力反射
					Id id = fields[i].getAnnotation(Id.class);
					if(id !=null && id.value().equals("auto")) {
						//走数据库自增
						continue;
					}
					Ignore ignore = fields[i].getAnnotation(Ignore.class);
					if(ignore !=null) {
						continue;
					}
					Object value = fields[i].get(entity);
					Date date = fields[i].getAnnotation(Date.class);
					if(date !=null && date.type().equals("auto")) {
						continue;
					} else if(date !=null && !date.type().equals("auto")) {
						//格式化我们需要的
						String format = DateUtil.format(new java.util.Date(),date.format());
						value = format;
					}
					objects.add(value);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			return objects.toArray();
		} else if (sqlFlag.equals(SQL_UPDATE)) {
			List<Object> objects = new ArrayList<>();
			Object[] tempArr = new Object[fields.length];
			for (int i = 0; tempArr != null && i < tempArr.length; i++) {
				try {
					fields[i].setAccessible(true); // 暴力反射
					tempArr[i] = fields[i].get(entity);
					Ignore ignore = fields[i].getAnnotation(Ignore.class);
					if(ignore !=null) {
						continue;
					}
					Date date = fields[i].getAnnotation(Date.class);
					if(date !=null && date.type().equals("auto")) {
						continue;
					} else if(date !=null && !date.type().equals("auto")) {
						//格式化我们需要的
						String format = DateUtil.format(new java.util.Date(),date.format());
						tempArr[i] = format;
					}
					objects.add(tempArr[i]);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			Object[] args = new Object[objects.size()];
			System.arraycopy(objects.toArray(), 1, args, 0, objects.size() - 1); // 数组拷贝
			args[args.length - 1] = tempArr[0];
			return args;
		} else if (sqlFlag.equals(SQL_DELETE)) {
			Object[] args = new Object[1]; // 长度是1
			fields[0].setAccessible(true); // 暴力反射
			try {
				args[0] = fields[0].get(entity);
			} catch (Exception e) {
				e.printStackTrace();
			}
			return args;
		}
		return null;

	}

	// 设置参数类型（写的不全，只是一些常用的）
	private int[] setArgTypes(T entity, String sqlFlag) {
		Field[] fields = entityClass.getDeclaredFields();
		if (sqlFlag.equals(SQL_INSERT)) {
			int[] argTypes = new int[fields.length];
			List<Integer> integerList = new ArrayList<>();
			try {
				for (int i = 0; argTypes != null && i < argTypes.length; i++) {
					fields[i].setAccessible(true); // 暴力反射
					Id id = fields[i].getAnnotation(Id.class);
					if(id !=null && id.value().equals("auto")) {
						//走数据库自增
						continue;
					}
					Ignore ignore = fields[i].getAnnotation(Ignore.class);
					if(ignore !=null) {
						continue;
					}
					Date date = fields[i].getAnnotation(Date.class);
					if(date !=null && date.type().equals("auto")) {
						continue;
					} else if(date !=null && !date.type().equals("auto")) {
						fields[i].set(entity,new java.util.Date());
					}
					if (fields[i].get(entity).getClass().getName().equals("java.lang.String")) {
						argTypes[i] = Types.VARCHAR;
					} else if (fields[i].get(entity).getClass().getName().equals("java.lang.Double")) {
						argTypes[i] = Types.DECIMAL;
					} else if (fields[i].get(entity).getClass().getName().equals("java.lang.Integer")) {
						argTypes[i] = Types.INTEGER;
					} else if (fields[i].get(entity).getClass().getName().equals("java.util.Date")) {
						argTypes[i] = Types.DATE;
					}
					integerList.add(argTypes[i]);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
			return integerList.stream().mapToInt(Integer::valueOf).toArray();
		} else if (sqlFlag.equals(SQL_UPDATE)) {
			int[] tempArgTypes = new int[fields.length];
			int[] argTypes =null;

			List<Integer> integerList = new ArrayList<>();
			try {
				for (int i = 0; tempArgTypes != null && i < tempArgTypes.length; i++) {
					fields[i].setAccessible(true); // 暴力反射
					Ignore ignore = fields[i].getAnnotation(Ignore.class);
					if(ignore !=null) {
						continue;
					}
					Date date = fields[i].getAnnotation(Date.class);
					if(date !=null && date.type().equals("auto")) {
						continue;
					} else if(date !=null && !date.type().equals("auto")) {
						fields[i].set(entity,new java.util.Date());
					}
					if (fields[i].get(entity).getClass().getName().equals("java.lang.String")) {
						tempArgTypes[i] = Types.VARCHAR;
					} else if (fields[i].get(entity).getClass().getName().equals("java.lang.Double")) {
						tempArgTypes[i] = Types.DECIMAL;
					} else if (fields[i].get(entity).getClass().getName().equals("java.lang.Integer")) {
						tempArgTypes[i] = Types.INTEGER;
					} else if (fields[i].get(entity).getClass().getName().equals("java.util.Date")) {
						tempArgTypes[i] = Types.DATE;
					}
					integerList.add(tempArgTypes[i]);
				}
				argTypes = new int[integerList.size()];
				System.arraycopy(integerList.stream().mapToInt(Integer::valueOf).toArray(), 1, argTypes, 0, integerList.size() - 1); // 数组拷贝
				argTypes[argTypes.length - 1] = tempArgTypes[0];
			} catch (Exception e) {
				e.printStackTrace();
			}
			return argTypes;

		} else if (sqlFlag.equals(SQL_DELETE)) {
			int[] argTypes = new int[1]; // 长度是1
			try {
				fields[0].setAccessible(true); // 暴力反射
				if (fields[0].get(entity).getClass().getName().equals("java.lang.String")) {
					argTypes[0] = Types.VARCHAR;
				} else if (fields[0].get(entity).getClass().getName().equals("java.lang.Integer")) {
					argTypes[0] = Types.INTEGER;
				}

			} catch (Exception e) {
				e.printStackTrace();
			}
			return argTypes;
		}
		return null;
	}

	private List<T> find(int pageNo, int pageSize, Map<String, String> where, LinkedHashMap<String, String> orderby) {
		// where 与 order by 要写在select * from table 的后面，而不是where rownum<=? )
		// where rn>=?的后面
		StringBuffer sql = new StringBuffer(
				" SELECT * FROM (SELECT t.*,ROWNUM rn FROM (SELECT * FROM " + entityClass.getSimpleName());
		if (where != null && where.size() > 0) {
			sql.append(" WHERE "); // 注意不是where
			for (Map.Entry<String, String> me : where.entrySet()) {
				String columnName = me.getKey();
				String columnValue = me.getValue();
				sql.append(columnName).append(" ").append(columnValue).append(" AND "); // 没有考虑or的情况
			}
			int endIndex = sql.lastIndexOf("AND");
			if (endIndex > 0) {
				sql = new StringBuffer(sql.substring(0, endIndex));
			}
		}
		if (orderby != null && orderby.size() > 0) {
			sql.append(" ORDER BY ");
			for (Map.Entry<String, String> me : orderby.entrySet()) {
				String columnName = me.getKey();
				String columnValue = me.getValue();
				sql.append(columnName).append(" ").append(columnValue).append(",");
			}
			sql = sql.deleteCharAt(sql.length() - 1);
		}
		sql.append(" ) t WHERE ROWNUM<=? ) WHERE rn>=? ");
		System.out.println("SQL=" + sql);
		Object[] args = { pageNo * pageSize, (pageNo - 1) * pageSize + 1 };
		RowMapper<T> rowMapper = BeanPropertyRowMapper.newInstance(entityClass);
		return dao.query(sql.toString(), args, rowMapper);
	}

	private int count(Map<String, String> where) {
		StringBuffer sql = new StringBuffer(" SELECT COUNT(*) FROM " + entityClass.getSimpleName());
		if (where != null && where.size() > 0) {
			sql.append(" WHERE ");
			for (Map.Entry<String, String> me : where.entrySet()) {
				String columnName = me.getKey();
				String columnValue = me.getValue();
				sql.append(columnName).append(" ").append(columnValue).append(" AND "); // 没有考虑or的情况
			}
			int endIndex = sql.lastIndexOf("AND");
			if (endIndex > 0) {
				sql = new StringBuffer(sql.substring(0, endIndex));
			}
		}
		System.out.println("SQL=" + sql);
		return dao.queryForInt(sql.toString());
	}


}
